#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
IR 方法节点定义
"""

import ast
from dataclasses import dataclass, field
from .function import IRFunctionNode
from ..base import Parameter
from ..statements import IRStmtBlock
from ...symbol_meta import Symbol, SymbolKind, Signature


def _strip_forward_ref_quotes(type_str: str) -> str:
    """去掉前向引用类型注解中的引号

    Args:
        type_str: 类型注解字符串，可能包含前向引用的引号

    Returns:
        去掉引号后的类型字符串

    Examples:
        "'Point'" -> "Point"
        '"Point"' -> "Point"
        "int" -> "int"
    """
    type_str = type_str.strip()
    if (type_str.startswith("'") and type_str.endswith("'")) or (
        type_str.startswith('"') and type_str.endswith('"')
    ):
        return type_str[1:-1]
    return type_str


@dataclass
class IRMethodNode(IRFunctionNode):
    """类方法IR节点，继承自IRFunctionNode"""

    is_property: bool = field(default=False)
    access_modifier: str = field(default="public")  # "public", "private", "protected"
    node_type: str = "method"  # 类属性

    def new_symbol(self) -> "Symbol":
        """创建方法的符号

        注意：签名创建被延迟到真正需要时（延迟初始化模式）
        这避免了在 build_symbol_cache 阶段的循环依赖问题
        """
        symbol = Symbol(
            self.name, SymbolKind.METHOD, ir_node=self, is_static=self.is_static
        )

        # 不在这里创建签名，让 Symbol.ensure_signatures_initialized() 延迟创建
        # 这样在符号缓存构建阶段就不会触发 parse_type，避免循环依赖

        return symbol

    def _generate_emit_template(self, args: list[str], has_self: bool) -> str:
        """生成方法的 emit_template

        Args:
            args: 参数名称列表（包括 self）
            has_self: 是否有 self 参数

        Returns:
            emit_template 字符串

        示例：
            - __eq__(self, other) -> "{self}.__eq__({other})"
            - add(self, x, y) -> "{self}.add({x}, {y})"
            - static_method(x) -> "ClassName::static_method({x})"
        """
        if has_self:
            # 实例方法：{self}.method_name(arg1, arg2, ...)
            # 去除 self，剩下的参数用逗号连接
            other_args = args[1:]  # 跳过 self
            args_str = ", ".join(f"{{{arg}}}" for arg in other_args)
            return f"{{{{self}}}}.{self.name}({args_str})"
        else:
            # 静态方法：ClassName::method_name(arg1, arg2, ...)
            args_str = ", ".join(f"{{{arg}}}" for arg in args)
            class_name = self.parent.name if self.parent else "UnknownClass"
            return f"{class_name}::{self.name}({args_str})"

    def create_signature(self) -> Signature:
        """创建方法签名

        param_types 包含所有参数类型（包括 self 的类型），与 builtin 配置保持一致
        对于普通方法，self 的类型是所属类的名称
        对于静态方法，没有 self 参数
        """
        from mcpy.symbol_meta import Signature

        module_typevars = self._get_module_typevars()
        param_types = []
        args = []
        template_params = []
        template_param_mapping = {}

        # 确定是否有 self 参数
        has_self = (
            not self.is_static
            and len(self.params) > 0
            and self.params[0].name == "self"
        )

        # 处理所有参数
        for i, param in enumerate(self.params):
            args.append(param.name)

            # 确定参数类型
            if i == 0 and has_self:
                # self 参数：使用所属类的名称作为类型（不需要规范化）
                param_type = self.parent.name if self.parent else "Any"
            else:
                param_type_str = param.type_annotation or "Any"

                # 规范化类型名称（处理类型别名如 List -> list）
                # 注意：不能调用 parse_type，因为会触发符号查找造成循环依赖
                param_type = self._normalize_type_name(param_type_str)

            param_types.append(param_type)

            # 检查是否为模板参数（对于 self 参数，使用 param_type；对于其他参数，使用原始字符串）
            type_to_check = (
                param_type
                if (i == 0 and has_self)
                else (param.type_annotation or "Any")
            )
            if type_to_check in module_typevars:
                if type_to_check not in template_params:
                    template_params.append(type_to_check)
                    # 记录模板参数位置（相对于 param_types 的索引）
                    template_param_mapping[type_to_check] = {
                        "param_index": len(param_types) - 1
                    }

        # 提取返回类型
        return_type_str = self.return_type or "None"

        # 规范化返回类型名称
        return_type = self._normalize_type_name(return_type_str)

        # 检查返回类型是否是 TypeVar（使用原始字符串检查）
        if return_type_str in module_typevars:
            if return_type_str not in template_params:
                template_params.append(return_type_str)

        # 生成默认的 emit_template（仅用于操作符重载）
        # 只为特殊方法（以 __ 开头和结尾）生成模板
        # 普通方法通过 CallEmitter 直接生成方法调用，不需要模板
        emit_template = None
        if self.name.startswith("__") and self.name.endswith("__"):
            emit_template = self._generate_emit_template(args, has_self)

        return Signature(
            function_name=self.name,
            param_types=param_types,
            return_type=return_type,
            template_params=template_params,
            template_param_mapping=template_param_mapping,
            args=args,
            is_method=True,
            emit_template=emit_template or "",
        )

    def build_from_ast(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> None:
        """从 AST 函数定义节点构建 IR 方法节点内容

        Args:
            node: AST 函数定义节点
        """
        super().build_from_ast(node)

        # 提取参数信息
        params = []
        for arg in node.args.args:
            type_ann = ""
            if arg.annotation:
                type_ann = ast.unparse(arg.annotation)
                # 去掉前向引用的引号
                type_ann = _strip_forward_ref_quotes(type_ann)

            param = Parameter(
                name=arg.arg,
                type_annotation=type_ann,
                parent=self,
            )
            params.append(param)

        # 提取装饰器信息
        decorators = []
        for decorator in node.decorator_list:
            if isinstance(decorator, ast.Name):
                # 简单装饰器：@decorator_name
                decorators.append({"name": decorator.id, "args": [], "kwargs": {}})
            elif isinstance(decorator, ast.Call):
                # 带参数的装饰器：@decorator_name(args)
                decorator_name = ""
                if isinstance(decorator.func, ast.Name):
                    decorator_name = decorator.func.id
                elif isinstance(decorator.func, ast.Attribute):
                    decorator_name = ast.unparse(decorator.func)

                # 提取位置参数
                args = []
                for arg in decorator.args:
                    if isinstance(arg, ast.Constant):
                        args.append(arg.value)
                    else:
                        args.append(ast.unparse(arg))

                # 提取关键字参数
                kwargs = {}
                for keyword in decorator.keywords:
                    if isinstance(keyword.value, ast.Constant):
                        kwargs[keyword.arg] = keyword.value.value
                    else:
                        kwargs[keyword.arg] = ast.unparse(keyword.value)

                decorators.append(
                    {"name": decorator_name, "args": args, "kwargs": kwargs}
                )
            else:
                # 其他复杂装饰器形式
                decorators.append(
                    {"name": ast.unparse(decorator), "args": [], "kwargs": {}}
                )

        # 检查是否为静态方法（包括@staticmethod和@classmethod）
        is_static = any(
            dec["name"] in ["staticmethod", "classmethod"] for dec in decorators
        )

        # 设置方法节点属性
        self.name = node.name
        self.source_line = getattr(node, "lineno", 0)
        self.decorators = decorators
        self.params = params
        self.return_type = ast.unparse(node.returns) if node.returns else None
        self.is_async = isinstance(node, ast.AsyncFunctionDef)
        self.is_static = is_static
        self.doc = ast.get_docstring(node) or ""

        self.body = IRStmtBlock(parent=self)
        self.body.build_from_ast(node.body)

        # 如果是构造函数，提取成员属性并添加到父类中
        from .class_node import IRClassNode

        if (
            self.name == "__init__"
            and self.parent
            and isinstance(self.parent, IRClassNode)
        ):
            self._extract_constructor_members()
            # 保持 __init__ 方法的原始名称，不要改为类名
            # 为 C++ 代码生成保存类名到扩展参数中
            self.set_extension_param("cpp_constructor_name", self.parent.name)
            self.set_extension_param("cpp_return_type", "")

    def get_cpp_method_name(self) -> str:
        """获取C++方法名称

        对于构造函数，返回类名；对于普通方法，返回原始方法名
        """
        if self.name == "__init__" and self.has_extension_param("cpp_constructor_name"):
            return self.get_extension_param("cpp_constructor_name")
        return self.name

    def get_cpp_return_type(self) -> str:
        """获取C++返回类型

        对于构造函数，返回空字符串；对于普通方法，使用父类逻辑
        """
        # 如果是构造函数，返回空字符串（构造函数没有返回类型）
        if self.name == "__init__":
            return ""
        # 对于普通方法，使用父类的逻辑
        return super().get_cpp_return_type()

    def _extract_constructor_members(self):
        """从构造函数中提取成员属性并添加到父类中"""
        from ..statements import IRAssign
        from ..expressions import IRAttribute, IRName
        from .property import IRPropertyNode

        if not self.body:
            return

        member_assignments: dict[str, IRAssign] = {}

        def visit_node(node):
            """递归访问节点查找成员变量赋值"""
            if isinstance(node, IRAssign):
                target_expr = node.get_target_expr()
                if target_expr and isinstance(target_expr, IRAttribute):
                    # 检查是否是 self.attribute 形式
                    if (
                        isinstance(target_expr.value, IRName)
                        and target_expr.value.id == "self"
                    ):
                        attr_name = target_expr.attr
                        member_assignments[attr_name] = node

            # 递归访问子节点
            for child in node.get_child_nodes():
                visit_node(child)

        # 访问方法体
        visit_node(self.body)

        from .class_node import IRClassNode

        if self.parent and isinstance(self.parent, IRClassNode):
            existing_props = {prop.name for prop in self.parent.properties}

            for member_name, assignment in member_assignments.items():
                if member_name in existing_props:
                    continue

                # 创建属性节点
                # 优先使用赋值语句的类型注解
                type_annotation = ""
                if hasattr(assignment, "annotation") and assignment.annotation:
                    type_annotation = assignment.annotation
                else:
                    # 如果赋值语句没有类型注解，尝试从构造函数参数推导
                    # 检查赋值的右侧是否是参数名
                    if hasattr(assignment, "value") and assignment.value:
                        from ..expressions import IRName

                        if isinstance(assignment.value, IRName) and assignment.value.id:
                            param_name = assignment.value.id
                            # 查找对应的参数
                            for param in self.params:
                                if param.name == param_name and param.type_annotation:
                                    type_annotation = param.type_annotation
                                    break

                prop_node = IRPropertyNode(
                    name=member_name,
                    source_line=assignment.source_line,
                    type_annotation=type_annotation,
                    associated_assignment=assignment,  # 保存赋值语句引用用于类型推断
                    parent=self.parent,
                )

                if member_name.startswith("_"):
                    prop_node.access_modifier = "private"
                else:
                    prop_node.access_modifier = "public"

                self.parent.properties.append(prop_node)

    def format_cpp_params(self) -> str:
        """格式化方法参数列表用于方法声明，跳过 self 参数"""
        if not self.params:
            return ""

        # 跳过第一个参数（self）
        cpp_params = (
            self.params[1:]
            if len(self.params) > 0 and self.params[0].name == "self"
            else self.params
        )

        return ", ".join(
            [f"{param.get_cpp_type()} {param.name}" for param in cpp_params]
        )
